package com.syf.core.engine;

import com.syf.core.config.SpringConfigBeanArgumentLoader;
import com.syf.core.config.SyApplicationConfiguration;
import com.syf.core.cont.SyfCoreCont;
import com.syf.core.kit.SyToolKitProvider;
import com.syf.core.server.SyServerBoostrap;
import com.syf.core.service.invoke.SyServiceInvoker;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;

/**
 * 完成书昀框架重要bean的定义，最终借助IOC完成加载.
 * <p>
 * 通过实现 BeanFactoryPostProcessor 接口，可以实现以下功能：
 * <p>
 * 修改Bean定义：可以在BeanFactory加载Bean定义后，对Bean定义进行修改。例如，可以根据某些条件动态修改Bean的属性值、更改Bean的作用域等。
 * 注册新的Bean定义：可以在BeanFactory加载Bean定义后，向BeanFactory中注册新的Bean定义。这样，可以动态地向Spring容器中添加新的Bean定义，从而实现动态扩展。
 * 添加自定义元数据：可以向Bean定义中添加自定义的元数据，以供后续的处理器使用。这样，其他处理器或组件可以根据这些元数据进行相应的处理
 */
public class SyFrameDefiner implements BeanDefinitionRegistryPostProcessor {
    private SyApplicationConfiguration configuration;

    private static final String CONFIGURATION_ATTACH_NAME = "syf-config";

    private SyFrameContext syFrameContext;

    public SyFrameDefiner(SyApplicationConfiguration configuration) {
        this.configuration = configuration;
    }

    /**
     * BeanDefinitionRegistryPostProcessor扩展点，额外注册bean definition
     * <p>
     * Step-2
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 定义SyFrameContext，为业务系统提供框架功能
        BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(SyFrameContext.class).getBeanDefinition();
        registry.registerBeanDefinition(SyfCoreCont.SYF_CONTEXT_BEAN_NAME, beanDefinition);
    }

    /**
     * BeanFactoryPostProcessor扩展点，允许在加载bean definition后，bean实例化前进行操作
     * <p>
     * Step-3
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 1、初始化SyFrameContext
        syFrameContext = beanFactory.getBean(SyfCoreCont.SYF_CONTEXT_BEAN_NAME, SyFrameContext.class);
        syFrameContext.insertSyApplicationConfiguration(configuration);

        // 2、将@Configuration对象的默认值设置回environment
        beanFactory.addBeanPostProcessor(new SpringConfigBeanArgumentLoader(configuration));

        // 3、将toolkit注册，以便syService中可以使用
        SyToolKitProvider syToolKitProvider = new SyToolKitProvider();
        beanFactory.addBeanPostProcessor(syToolKitProvider);
        syFrameContext.insertToolKitProvider(syToolKitProvider);

        // 4、将service invoker设置到SyFrameContext中
        beanFactory.addBeanPostProcessor(new SyServiceActuatorInsertCtx(this));

        // 5、将sy server启动
        SyServerBoostrap syServerBoostrap = new SyServerBoostrap();
        beanFactory.addBeanPostProcessor(syServerBoostrap);

        //  ^_^ 可以考虑卸载你不想要的spring bean ^_^

    }

    private void insertServiceActuator2FrameCtx(SyServiceInvoker serviceInvoker) {
        syFrameContext.insertServiceInvoker(serviceInvoker);
    }

    /**
     * 搞的这么费劲，只是希望将service invoker设置到SyFrameContext中的方法设置为protected，而不希望是public
     * */
    public static class SyServiceActuatorInsertCtx implements BeanPostProcessor {
        private final SyFrameDefiner syFrameDefiner;

        public SyServiceActuatorInsertCtx(SyFrameDefiner syFrameDefiner) {
            this.syFrameDefiner = syFrameDefiner;
        }

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (SyfCoreCont.BEAN_NAME_OF_ACTUATOR.equals(beanName) && SyServiceInvoker.class.isAssignableFrom(bean.getClass())) {
                syFrameDefiner.insertServiceActuator2FrameCtx((SyServiceInvoker) bean);
            }

            return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
        }
    }
}
